#Copyright (C) Nial Peters 2012
#
#This file is part of AvoScan.
#
#AvoScan is free software: you can redistribute it and/or modify
#it under the terms of the GNU General Public License as published by
#the Free Software Foundation, either version 3 of the License, or
#(at your option) any later version.
#
#AvoScan is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#GNU General Public License for more details.
#
#You should have received a copy of the GNU General Public License
#along with AvoScan.  If not, see <http://www.gnu.org/licenses/>.
"""
The ports module provides the SerialPortManager class which deals with
mapping the system names given to the serial ports on the scanner (if it 
has any) to the devices which they are connected to.
"""

class HardwareUnavailableError(Exception):
    """
    Exception raised when the serial ports on the scanner cannot be detected - 
    probably because they don't exist.
    """
    pass

try:
    import serial
    from serial.tools import list_ports
    
   
    class SerialPortManager:
        """
        Class to deal with mapping the system names given to the (possibly) four
        serial ports on the scanner, with the devices they are connected to. If 
        no serial support is installed (pyserial is missing or is too old a version) 
        then instanciating this class will raise HardwareUnavailableError.
        """
        def __init__(self, scanner):
            scanner_id = scanner.get_serial_num()
            
            try:
                hub_serial_num = _serial_mappings[scanner_id]
            except KeyError:
                raise HardwareUnavailableError, "No serial ports available for this scanner."
            
            
            self.ports = list(sorted(list_ports.grep(hub_serial_num)))
            
            if len(self.ports) < 4:
                raise HardwareUnavailableError, "Less than four ports detected - something bad has happened!"
            
            #we expect that the first port found will be the loopback port        
            try:
                ser = serial.Serial(self.ports[0][0], timeout=2)
                ser.write("Hello Avoscan!")
                a = ser.read(14)
                ser.close()
            except serial.SerialException, e:
                raise HardwareUnavailableError, e.args[0]
            
            if a != "Hello Avoscan!":
                raise HardwareUnavailableError, "Incorrect response from loopback port."
            
        
        def getExternalPort(self):
            """
            Returns the name (e.g. COM1 or /dev/ttyS0) of the serial port that is connected
            to the external D9 connector on the scanner.
            """
            return self.ports[3][0]
        
        
        def getCameraPort(self):
            """
            Returns the name (e.g. COM1 or /dev/ttyS0) of the serial port that is connected
            to the 4 pin mini-connector for the camera.
            """
            return self.ports[2][0]
        
        
        def get_compass_port(self):
            """
            Returns the name (e.g. COM1 or /dev/ttyS0) of the serial port that is connected
            to internally to the compass chip.
            """
            return self.ports[1][0]
        
        
        def print_port_list(self):
            """
            Prints a list of all the serial ports detected.
            """
            for port, desc, hwid in self.ports:
                print "%-20s" % (port,)
                print "    desc: %s" % (desc,)
                print "    hwid: %s\n" % (hwid,)
        
except ImportError:
    #pyserial is not installed, or is too old
    class SerialPortManager:
        def __init__(self, scanner):
            raise HardwareUnavailableError, "pyserial is not installed or is an old version."
    
#mappings from Phidget Stepper controller serial number, to USB hub serial number    
_serial_mappings = {267800:'FTVB1PAE'
                    }